import transform
from numpy import array, dot
from OpenGL.GL import *

class WireBox(object):
    """
    WireBox makes it easy to render wireframe boxes in OpenGL.
    Probably only a single WireBox object is needed in one program, which
    then can be used to draw any number of boxes with different transformation
    matrices.
    """

    def __init__(self, mTransform = None, color = None):
        super(WireBox, self).__init__()

        # set up model transform for this box
        if mTransform == None:
            self.mTransform = transform.identity()
        else:
            self.mTransform = mTransform

        # set up color for this box
        if color == None:
            self.color = [1,1,1,1]
        else:
            self.color = color

        # store some data to use later
        self.vshader_src = """
            #version 110
            uniform mat4 pmvTransform;
            void main()
            {
                gl_Position = pmvTransform * gl_Vertex;
            }
            """
        self.fshader_src = """
            #version 110
            uniform vec4 color;
            void main()
            {
                gl_FragColor = color;
            }
            """
        self.vertex = array([0, 0, 0, 1,
                             1, 0, 0, 1,
                             0, 1, 0, 1,
                             1, 1, 0, 1,
                             0, 0, 1, 1,
                             1, 0, 1, 1,
                             0, 1, 1, 1,
                             1, 1, 1, 1], dtype='float32')
        self.edge = array([0, 1,
                           0, 2,
                           0, 4,
                           1, 3,
                           1, 5,
                           2, 3,
                           2, 6,
                           3, 7,
                           4, 5,
                           4, 6,
                           5, 7,
                           6, 7], dtype='uint32')
        self.glStatus = False

    def initialized(self):
        return self.glStatus

    def initializeGL(self):
        # first compile shader program
        vshader_id = glCreateShader(GL_VERTEX_SHADER)
        glShaderSource(vshader_id, self.vshader_src)
        glCompileShader(vshader_id)

        fshader_id = glCreateShader(GL_FRAGMENT_SHADER)
        glShaderSource(fshader_id, self.fshader_src)
        glCompileShader(fshader_id)

        self.shader = glCreateProgram()
        glAttachShader(self.shader, vshader_id)
        glAttachShader(self.shader, fshader_id)
        glLinkProgram(self.shader)

        self.pmvLocation = glGetUniformLocation(self.shader, "pmvTransform")
        self.colorLocation = glGetUniformLocation(self.shader, "color")

        # then set up VBO stuff
        self.meshbuffers = glGenBuffers(2)
        glBindBuffer(GL_ARRAY_BUFFER, self.meshbuffers[0])
        glBufferData(GL_ARRAY_BUFFER, self.vertex, GL_STATIC_DRAW)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.meshbuffers[1])
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, self.edge, GL_STATIC_DRAW)

        # remember we've done this
        self.glStatus = True

    def boundingBox(self):
        "Return a bounding box of the geometry as a pair xmin,xmax of triples."
        # transform the corners of the unit box, do mins and maxes
        p = [dot(self.mTransform, [0,0,0,1]),
             dot(self.mTransform, [1,0,0,1]),
             dot(self.mTransform, [0,1,0,1]),
             dot(self.mTransform, [1,1,0,1]),
             dot(self.mTransform, [0,0,1,1]),
             dot(self.mTransform, [1,0,1,1]),
             dot(self.mTransform, [0,1,1,1]),
             dot(self.mTransform, [1,1,1,1])]
        xmin = [p[0][0]/p[0][3], p[0][1]/p[0][3], p[0][2]/p[0][3]]
        xmax = xmin
        for i in range(1,8):
            for j in range(3):
                x = p[i][j]/p[i][3]
                xmin[j] = min(xmin[j], x)
                xmax[j] = max(xmax[j], x)
        return xmin, xmax

    def paintGL(self, pvTransform):
        # get total transformation for this box (project - view - model)
        pmvTransform = transform.compose(pvTransform, self.mTransform)

        glUseProgram(self.shader)
        glUniformMatrix4fv(self.pmvLocation, 1, True, pmvTransform.flatten())
        glUniform4f(self.colorLocation, self.color[0], self.color[1],
                                        self.color[2], self.color[3])
        glEnableClientState(GL_VERTEX_ARRAY)
        glBindBuffer(GL_ARRAY_BUFFER, self.meshbuffers[0])
        glVertexPointer(4, GL_FLOAT, 0, None)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.meshbuffers[1])
        glDrawElements(GL_LINES, len(self.edge), GL_UNSIGNED_INT, None)
        glDisableClientState(GL_VERTEX_ARRAY)

